package com.salmon.study.iot.group;

import akka.actor.AbstractActor;
import akka.actor.ActorRef;
import akka.actor.Props;
import akka.actor.Terminated;
import com.salmon.study.iot.Device;
import com.salmon.study.iot.group.message.ReplyDeviceList;
import com.salmon.study.iot.group.message.RequestDeviceList;
import com.salmon.study.iot.group.message.RequestTrackDevice;
import lombok.extern.slf4j.Slf4j;
import java.util.HashMap;
import java.util.Map;

@Slf4j
public class DeviceGroup extends AbstractActor {

    private final String groupId;


    public DeviceGroup(String groupId) {
        this.groupId = groupId;
    }

    public static Props props(String groupId) {
        return Props.create(DeviceGroup.class, groupId);
    }

    private final Map<String, ActorRef> deviceIdToActor = new HashMap<>();

    private final Map<ActorRef, String> actorToDeviceId = new HashMap<>();

    @Override
    public void preStart() {
        log.info("DeviceGroup {} started", groupId);
    }

    @Override
    public void postStop() {
        log.info("DeviceGroup {} stopped", groupId);
    }


    @Override
    public Receive createReceive() {
        return receiveBuilder().match(RequestTrackDevice.class, this::onTrackDevice)
                .match(RequestDeviceList.class, this::onDeviceList)
                .match(Terminated.class, this::onTerminated).build();
    }

    private void onDeviceList(RequestDeviceList r) {
        sender().tell(new ReplyDeviceList(r.getRequestId(), deviceIdToActor.keySet()), self());
    }

    private void onTrackDevice(RequestTrackDevice trackMsg) {
        if (this.groupId.equals(trackMsg.getGroupId())) { // 组ID必须匹配
            ActorRef deviceActor = deviceIdToActor.get(trackMsg.getDeviceId());
            if (deviceActor != null) {
                // forward tell 区别
                // forward keeps the original sender
                // tell 把消息发送者设置为 current Actor
                deviceActor.forward(trackMsg, getContext());
            } else {
                log.info("Creating device actor for {}", trackMsg.getDeviceId());
                // 如果Device不存在，则创建出来并保存到Group中
                deviceActor = getContext().actorOf(Device.props(groupId, trackMsg.getDeviceId()), "device-" + trackMsg.getDeviceId());

                getContext().watch(deviceActor); // 监控deviceActor

                deviceIdToActor.put(trackMsg.getDeviceId(), deviceActor);

                this.actorToDeviceId.put(deviceActor, trackMsg.getDeviceId());

                deviceActor.forward(trackMsg, getContext());
            }
        } else {
            log.warn("Ignoring TrackDevice request for group is {}. This actor is responsible for group is {}.",
                    groupId, this.groupId
            );
        }
    }

    private void onTerminated(Terminated terminated) {
        ActorRef deviceActor = terminated.getActor();
        String deviceId = actorToDeviceId.get(deviceActor);
        log.info("Device actor for {} has been terminated", deviceId);
        actorToDeviceId.remove(deviceActor);
        deviceIdToActor.remove(deviceId);
    }
}
